#include "userprog/syscall.h"
#include <stdio.h>
#include <syscall-nr.h>
#include "threads/interrupt.h"
#include "threads/thread.h"
#include "userprog/pagedir.h"
#include "userprog/process.h"
#include "threads/vaddr.h"
#include "devices/shutdown.h"

/* Validates that a user pointer is within the user address space,
   mapped to a valid page, and not NULL. */
static void validate_user_pointer(const void *uaddr) {
    if (uaddr == NULL || !is_user_vaddr(uaddr) ||
        pagedir_get_page(thread_current()->pagedir, uaddr) == NULL) {
        sys_exit(-1); // Terminate process on invalid pointer.
    }
}

/* Reads a word (4 bytes) from user memory safely. */
static uint32_t read_user_word(const uint32_t *uaddr) {
    validate_user_pointer(uaddr);
    return *(uint32_t *)uaddr;
}

/* Fetches system call arguments. */
static void fetch_syscall_args(struct intr_frame *f, int *args, int count) {
    for (int i = 0; i < count; i++) {
        args[i] = read_user_word((uint32_t *)f->esp + i + 1);
    }
}

/* Handles a system call interrupt. */
static void syscall_handler(struct intr_frame *f) {
    uint32_t *esp = f->esp;

    /* Validate user stack pointer. */
    validate_user_pointer(esp);

    int syscall_num = read_user_word(esp);

    switch (syscall_num) {
        case SYS_HALT: {
            shutdown_power_off();
            break;
        }
        case SYS_EXIT: {
            int status;
            fetch_syscall_args(f, &status, 1);
            sys_exit(status);
            break;
        }
        case SYS_EXEC: {
            char *cmd_line;
            fetch_syscall_args(f, (int *)&cmd_line, 1);
            validate_user_pointer(cmd_line);
            f->eax = process_execute(cmd_line);
            break;
        }
        case SYS_WAIT: {
            tid_t child_tid;
            fetch_syscall_args(f, &child_tid, 1);
            f->eax = process_wait(child_tid);
            break;
        }
        default:
            printf("Unknown system call: %d\n", syscall_num);
            sys_exit(-1);
    }
}

/* Initializes system calls. */
void syscall_init(void) {
    intr_register_int(0x30, 3, INTR_ON, syscall_handler, "syscall");
}

/* Terminates the current process. */
void sys_exit(int status) {
    struct thread *cur = thread_current();
    cur->exit_status = status;
    printf("%s: exit(%d)\n", cur->name, status);
    thread_exit();
}
